■ PCのキーボードスイッチを読み込んで液晶に表示する                             
 <PS2概要>  
  (1) 全般
   パソコンのキーボードの信号はPS2と呼ばれるシリアルインターフェースで通信がおこなわれています。IBMがつくった
  2線式クロック同期方式双方向半二重シリアル通信で通信速度は9600bpsです。マウスにも使われています。キー
  ボードの各キーにはアスキーコードとはことなるスキャンコードと呼ばれるコードがわりあてられています。パソコンの
  場合キーボードが押されると以下のように信号がPCのアプリケーションソフトへと送られていきます。
     @ キーボードが押される
     Aキーボードマイコン(intel 8051等)がダイナミックスキャンにより押されたキーを特定する
     Bキーボードマイコンはスキャンコードを生成して同期クロック(CLK)と共にデータ(DATA)をPS2コネクタに
       出力する。
        ・クロックはDATAを送る時だけ発生し、クロックの速度は9600bpsです。 
        ・DATAは1〜4バイトで 1バイト毎に2msec程度の間隔で区切って送られてくる
        ・DATAに出力される信号はスキャンコード以外にACKやキーボードのIDなどもある。
     C信号がキーボードケーブル経由で伝送される。
     Dパソコン側ではキーボードコントローラ(intel 8042等)がPS2シリアル信号をスキャンコードに変換する
     EさらにこのスキャンコードをBIOSがASCIIコードに変換する
     FBIOS→OS→アプリケーションソフトとASCIIコードが伝送される

    【PS2インターフェースに関する技術情報】 
    OADGテクニカル・リファレンス 
    
Sazanami Online PS/2 インターフェイスの研究


  (2) コネクタ & ハードウェア
     ・ コネクタはミニDINと呼ばれるコネクタが使われています。
     ・ PC側のコネクタ(メス)のピンアサイン(ピン配列)を以下に示します。2番、6番はNCで4番が電源、3番が
       グランド、1番、5番が信号ラインとなっています。

ピン配列          
 1 … DATA 
 2 … NC
 3 … GND
 4 … +5V
 5 … CLK
 6 … NC         
  

     ・ 1番DATA信号と5番CLK信号のインターフェースはKeyBoard側、PC側共に電気回路的には全く同じで4.7KΩ
       プルアップによるNPNトランジスタオープンコレクタ回路になっています。入力と出力は別ポートの構成となって
       います。 したがって PC側の所要ポート数は 合計4ポートとなります。 (下図 左 参照)
        実際に製作したものは、少ないポート数でPS2インターフェースを実現する観点からPICの流れ出し電流許容値
       が25mAと大きいことを利用して信号ラインに直列にR=150Ωの抵抗を挿入することとしました。(下図 右参照)
         ★ KeyBoard側のトランジスタがON(ON電圧=0.4V)、PIC側のポートが出力モード”High”の場合を実測
           するとPIC側のポートの電圧は3.6Vでした。したがって、この時のPICの出力電流は 
              I=(3.6V-0.4V)/150Ω = 21.3mA  ( < 25mA    → OK )  
           また、PICの入力レベル”High”はTTLレベルの2.0V以上とと規定されています。この面からもチェック
           してみますと
              3.6V > 2.0V  ( → OK )
          となります。
         ★ 一方、KeyBoard側のトランジスタがOFFで PIC側が”Low”の場合のKeyBoard側の出力電圧を実測して
           みると めのこ計算値 0.54V (=150Ω/(150Ω+4.7KΩ)×(5V - 0.4V) + 0.4V)より小さな0.4Vで
           した。 KeyBoard側のLowレベルのスレッシュホールドレベルはTTLレベルかCMOSレベルかわかりま
                       せんが
             0.4V < 0.8V (TTLレベルの場合  → OK )
             0.4V < 1.5V (CMOSレベル場合 at Vcc=+5V  → OK )
           と問題ないことがわかります。


   (3) パルス信号
     (3−1) 全般
       ・ 信号はKeyBoard側からPC側、PC側からKeyBoard側の両方ともKeyBoard側のCLKからのクロックに同期して
         信号の授受がおこなわれます。クロックは9600bps→9.6KHz duty=50%で、信号授受をおこなう時だけ出力
         されます。
       ・ 1〜4バイトのデータ(可変長バイト)がやりとりされます。 複数のバイトデータが送られる時は1バイトづつ
         2msec程度の間隔をおいておくられます。1バイトのデータは下図のようにスタートビット、8ビットのデータ、
         パリティビット、ストップビットから構成されています。パリティは奇数パリティです。
          データはLSBから すなわちbit0、bit1、…bit7と送りだされます。 KeyBoard側よりPIC側へ信号が送られ
         る場合は CLKのパルスが立ち下がりとなった時にDATA信号は出力されているのでこのタイミングで
         割込みによってDATAを読み込みます。また PIC側よりKeyBoard側へ信号を送る時はCLKのパルスが
         立ち下がったことを検出した時は、1μsec以内にDATA信号をDATAラインに出力する。
         下図はKeyBoard側よりPIC側へ 11111010(2進表示:プログラム上の表記は0b11111010    16進表示で
         表記すれば 0xfa)と云うデータを送った場合の例を示しています。
 
 




   (3−2) スキャンコード
      キーボードの各キーにはアスキーコードとは異なるスキャンコードと呼ばれるコードが割り当てられています。
           キーボードを押した時(Make)と離した時(Brake)のそれぞれの瞬間にスキャンコードが送信されます。Brakeの
     スキャンコードはF0の後にMakeのスキャンコードが追加されたものです。 キーによってはMakeのスキャンコードが
     2バイトのものもあります。 したがってこの場合のBrakeのスキャンコードは、3バイトのデータが2msec間隔で
     つづけて送られてきます。     (日本語109キーボードのスキャンコード表 参照)
  

   <試作品仕様>
     ・  キーボードを押して離すと液晶(16文字:英数字&記号 × 2段)に押したキーボードの文字と送られてきた
      スキャンコードが16進表示で1バイトづつあとから送られてきた順番に表示されること
        ★上段 (左隅から) ……  最後に押したキーボードの文字      M[0]L[0]  M[1]L[1]  M[2]L[2]  M[3]L[3]   
        ★下段 (左隅から) ……          M[4]L[4]   M[5]L[5]  M[6]L[6]  M[7]L[7]  M[8]L[8] 

                    (注) M[i]L[i] ( i = 0〜8) : 最後から i番前に送られてきた1バイトデータを16進表示したもの
                           L[i]が下位4ビット、M[i]が上位4ビットをあらわす。

         (例) キーボードを”y” ”b ” と 押した後 ”k” を押して指を離すと液晶には以下のように表示される
                    k    42 f0 42 32
                                       f0 32 35 f0 35  
    
     ・ シフトキーを押しながらキーボードを押すとシフト文字(アルファベットの大文字など)が表示されること
     ・ Caps Lock ができること。尚、この時キーボードのLEDが点灯すること
                   
       


  <試作品外観>
    下記の写真には上記回路図にはない、また本テーマと関係のない部品が多々写っています。



<回路図>
    PIC18F452をつかった場合の回路図を以下に示します。(→回路図ののPDFファイル

 

                    

以下のプログラム例の中にある液晶表示器制御ライブラリ 1llcd_lib.cは 後閑哲也さんが設計されたものです。
   <プログラム例>

//--------------------------------------
//--------------------------------------
// 構成
// KeyBoard → PIC18F452 →  16文字キャラクタ液晶
//          ★ shift capsLock 対応
//  PICからキーボードへの送信
//   @ CLK線を60μsec以上インアクティブ(L)にする
//   A 次にDATA線も60μsec程度インアクティブ(L)にします
//   B DATA線、CLK線をア60μsec程度クティブ(H)にする
//   C スタートビットをDATA線に出力し DATA線にキーボードがクロックを出力するのを待つ
//   D CLK線にクロックの立下りを検出したらDATA線にデータを出力する
//   E ビット8には奇数パリティを計算して設定する


#include "18f452.h"
#use delay(clock=40000000)
#FUSES  EC,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP
#use RS232(BAUD=9600,XMIT=PIN_C6,RCV=PIN_C7)    // TX=RC6,RX=RC7
#byte   INTCON3=0xFF1   //アドレス0xFF1の変数をINTCON3と命名
#byte   TRISB=0xF93

#define Transmit        1
#define Receive         0               


//////// Port define and link LCD library 
#define mode     0


//  No1 液晶
#define input_x         input_D
#define output_x        output_D
#define set_tris_x      set_tris_D
#define rs PIN_D2       //chip select
#define stb PIN_D0      //strobe

#include <1lcd_lib.c>

#include "ScanTable1.h"

unsigned long int BitReceiveData = 0,BitSendData = 0;
int     BitReceiveCount = 0,ix,TRMode = Receive,BitSendCount =0;
char ScanData[100],AsciiData[100];
short int Flag,Shift = 0,Caps = 0,CapsNow = 0,CapsTrigger =0,LockTrigger = 0;
unsigned long int temp;


void Lcd()      //液晶表示
{
        lcd_cmd(0x01);                          // 全消去
        printf(lcd_data,"%c    %x %x %x %x",
        AsciiData[ix-1],ScanData[ix-1],ScanData[ix-2],ScanData[ix-3],ScanData[ix-4]);   // 他なら 文字表示
        lcd_cmd(0xC0);//2行目の先頭へ
        printf(lcd_data,"  %x %x %x %x %x",
        ScanData[ix-5],ScanData[ix-6],ScanData[ix-7],ScanData[ix-8],ScanData[ix-9]);
}

unsigned  long int Parityx(unsigned long int Datax) // パリティ計算
{
        unsigned long int i = 0,Parity = 0;
        for(i = 0; i < 8; i ++ )
        {
                if(bit_test(Datax,i) == 1)Parity = Parity + 1;
        }
        if((Parity % 2) == 1) Parity = 0;
        else Parity = 1;
        return Parity;
}

        
void BitSendDataSet()   //奇数パリティデータのセット 及び 送信データのセット
{
        unsigned long int Parity;
        
        Parity = Parityx(BitSendData);
        if(Parity == 1)bit_set(BitSendData,8);
        else bit_clear(BitSendData,8);
        BitSendData      =BitSendData  << 1;

}


void TransmitModeSet(){ //キーボード側に送信したい旨連絡する
                                set_tris_b(0x00);               //B0(DATA線)、 B1(CLK線)をともに出力ポートに設定
                                output_low(PIN_B1);             // CLK線→L
                                output_high(PIN_B0);
                                delay_us(60);
                                output_low(PIN_B0);             // DATA線→L
                                delay_us(60);                   //60μsecごとにキーボードはラインをチェックするので15msec待つ
                                                                                //キーボードi8042はホストがデータを送信しようとしていることを認識したはず
                
                
                                set_tris_b(0b00000010); //B0(DATA線)を出力ポートに B1(CLK線)を入力ポートに設定
                                                                                //キーボードからrのクロックに同期してB0からの出力の準備をおこなう
                                output_low(PIN_B0);             // DATA線→L
                                TRMode = Transmit;

}


void Transmitx()        //送信関数
{
        output_bit(PIN_B0,(bit_test(BitSendData,0)));
        BitSendData = BitSendData >> 1;
        BitSendCount++;
        if(BitSendCount >= 12){
                TRMode = Receive;       //
                BitSendCount = 0;
        }


}

void Receivex(){        //受信関数
        temp = (unsigned long int) input(PIN_B0);       // Clock H --> L
        if((Flag == 0) && (temp == 0))Flag = 1;
        if(Flag == 1)
        {
                BitReceiveData = (BitReceiveData >> 1) ;//このビットシフト回数は9回である。10回ではない
                temp = (temp << 10);
                BitReceiveData = BitReceiveData + temp ;

                BitReceiveCount++;

                if(BitReceiveCount >= 10)
                {
                        BitReceiveData = BitReceiveData >> 2;                   // 下2桁の無効データを削除
                        ScanData[ix] = (byte)(BitReceiveData & 0xFF);


                        //-------------------シフトキーがおされているかどうかの判別
                if( (ScanData[ix] == 0x12)||(ScanData[ix] == 0x59))Shift = 1;// シフトキーMake操作の場合
                if( ((ScanData[ix] == 0x12)||(ScanData[ix] == 0x59)) 
                                                                && (ScanData[ix-1] == 0xF0))Shift = 0;// シフトキーBreak操作の場合
//-----Capsロック判別

                        //-------Caps Lock キーが押されているかどうかの判別     
                if(ScanData[ix] == 0x58)
                {
                        CapsNow = 1;
                }
                if( (ScanData[ix] == 0x58) && (ScanData[ix-1] == 0xF0))// CapsLock : Brake
                {
                        CapsNow = 0;
                }

                if( ( (ScanData[ix] == 0x58) && 
                          !((ScanData[ix-1] == 0xf0) || (ScanData[ix-1] == 0x58) )  ) ||
                        ((ScanData[ix] == 0x58) && (ScanData[ix-1] == 0x58) && (ScanData[ix-2] == 0xf0))        )       
                {
                        CapsTrigger = 1;
                
                }
                else CapsTrigger = 0;

                //---------------Caps Lock 状態かどうかの判別
                if( (CapsTrigger == 1) && (Shift == 1))
                {
                        LockTrigger =  1;
                }
                else LockTrigger = 0;
        
        

                AsciiData[ix] = ScanToAscii(ScanData[ix],(Shift | Caps));
                                // L R Shift or Caps

                if((ScanData[ix] == 0x12) || (ScanData[ix] == 0x59) || (ScanData[ix] == 0x58))
                {
                        AsciiData[ix] = AsciiData[ix -2];
                }
                
                if( (ScanData[ix] <= 0x80) )
                {
                                lcd_cmd(0x01);                          // 全消去
                                printf(lcd_data,"%c    %x %x %x %x",
                                AsciiData[ix],ScanData[ix],ScanData[ix-1],ScanData[ix-2],ScanData[ix-3]);       // 他なら 文字表示
                                lcd_cmd(0xC0);//2行目の先頭へ
                                printf(lcd_data,"  %x %x %x %x %x",
                                ScanData[ix-4],ScanData[ix-5],ScanData[ix-6],ScanData[ix-7],ScanData[ix-8]);
                        
                }

                        ix++; if(ix >=100)ix =0;
                        BitReceiveCount = 0;
                        BitReceiveData = 0x0000;
                        Flag = 0;
                }
        }
}


#INT_EXT1                       //B1:Clk
void KeyBoardClk()
{
        switch(TRMode){
                case Receive:// スタンバイ状態 キーボードからの信号待ち
                        Receivex();
                        break;
                case Transmit:// キーボードへの送信モード
                        Transmitx();
                        break;
                default: break;
        }
}

void    CapsLockLed()   //CapsLockLedのON/OFF制御
{
                if(LockTrigger == 1)
                {
                        disable_interrupts(INT_EXT1);
                        disable_interrupts(GLOBAL);
        
                        BitSendData = 0xFFED;           
                        BitSendDataSet();
                        TransmitModeSet();

                        enable_interrupts(INT_EXT1); // Set up EXT1
                        enable_interrupts(GLOBAL);

                        do
                        {
                        }while(ScanData[ix-1] != 0xfa);
                        ScanData[ix-1] = 0xf8;


                        disable_interrupts(INT_EXT1);
                        disable_interrupts(GLOBAL);

                        if(Caps == 0)
                        {
                                BitSendData = 0xFF04;   //CapsLockLED ON
                                Caps = 1;                               //              
                        }
                        else
                        {
                                BitSendData = 0xFF00;   //CapsLockLED OFF
                                Caps = 0;
                        }       
                        BitSendDataSet();
                        TransmitModeSet();

                        enable_interrupts(INT_EXT1); // Set up EXT1
                        enable_interrupts(GLOBAL);

                        do
                        {
                        }while(ScanData[ix-1] != 0xfa);
        
                        if(ScanData[ix-2] == 0xf8)ScanData[ix-2] = 0xfa;

                        LockTrigger = 0;

                }

}

void InitialCheck()     //電源投入時のPICとキーボード間の送受信
{

//------- リセット命令 FF 
        BitSendData = 0xFFFF;   //キーボードリセット命令

        BitSendDataSet();       //パリティ計算を行い送信データをセットする
        TransmitModeSet();      //キーボード側に送信したい旨連絡し 送信モードでINT_EXT1にクロックがくるのを待つ
        enable_interrupts(INT_EXT1); // INT_EXT1が外部割込みを検出できるように割込みを許可する
        enable_interrupts(GLOBAL);

        do{                                     //最初にAck(0xFA : コマンドに対する正しく受け取られた応答)がキーボードからかえってくる
        }while(ScanData[ix-1] != 0xAA);//その後BAT(0xAA:Basic Assurance Test)が正しく終了したがキーボードからかえってくる 


        disable_interrupts(INT_EXT1);
        disable_interrupts(GLOBAL);

        BitSendData = 0xFFF2;   //識別情報(キーボードorマウス? どのようなキーボードか?) を要求
        BitSendDataSet();//パリティ計算を行い送信データをセットする
        TransmitModeSet();//キーボード側に送信したい旨連絡し 送信モードでINT_EXT1にクロックがくるのを待つ

        enable_interrupts(INT_EXT1); // INT_EXT1が外部割込みを検出できるように割込みを許可する
        enable_interrupts(GLOBAL);

        do{                                     //最初にAck(0xFA : コマンドに対する正しく受け取られた応答)がキーボードからかえってくる
        }while(ScanData[ix-1] != 0x83);//日本語キーボードの場合0xAB,0x83がかえってくる

        disable_interrupts(INT_EXT1);
        disable_interrupts(GLOBAL);

        BitSendData = 0xFFED;   //キーボードのLED点灯、消灯要求      
        BitSendDataSet();
        TransmitModeSet();

        enable_interrupts(INT_EXT1); // Set up EXT1
        enable_interrupts(GLOBAL);

        do{
        }while(ScanData[ix-1] != 0xfa);
        ScanData[ix-1] = 0xf8;

        disable_interrupts(INT_EXT1);
        disable_interrupts(GLOBAL);


        BitSendData = 0xFF00;           
        BitSendDataSet();
        TransmitModeSet();

        enable_interrupts(INT_EXT1); 
        enable_interrupts(GLOBAL);

        do{
        }while(ScanData[ix-1] != 0xfa);
        
        if(ScanData[ix-2] == 0xf8)ScanData[ix-2] = 0xfa;

        Lcd();  //液晶表示
}

main(){

        TRISB = 0xFF;//B port all input mode
        INTCON3 = 0b01000000;// 割込みレベル : 高位 (bit7)(bit6)(bit5)(bit4)(bit3)(bit2)(bit1)(bit0) 
                        // = (X2割込レベル)(X1割込レベル)(意味無)(X2割込許可)(X1割込許可)(意味無)(X2割込フラグ)(X1割込フラグ)
        ext_int_edge( 1, H_TO_L); // PIN_B1 からの外部割込み

        lcd_init();
        lcd_cmd(0b00001100);                    // カーソル:OFF    ブリンク:OFF
        lcd_clear();
        printf(lcd_data,"start!!");
        delay_ms(1000); // KeyBoard POR timeは MAX500msec

        InitialCheck(); //電源投入時のPICとキーボード間の送受信

        while(1)
        {
                CapsLockLed();  //CapsLockLedのON/OFF制御
        }

        return 0;
}

//-------------------------------------------------------------------------------------------------
//*******************************************
//インクルードファイル    ScanTable1.h
//*******************************************

char ShiftAscii(char ScanData)
{       
        char temp;
        switch(ScanData)
        {       
                case 0x16:      temp = '!';     break;
                case 0x1E:      temp = '"';     break;
                case 0x26:      temp = '#';     break;
                case 0x25:      temp = '$';     break;
                case 0x2E:      temp = '%';     break;
                case 0x36:      temp = '&';     break;
                case 0x3D:      temp = 0x27; break;
                case 0x3E:      temp = '(';     break;
                case 0x46:      temp = ')';     break;
                case 0x45:      temp = 0x7E; break;
                case 0x1C:      temp = 'A';     break;
                case 0x32:      temp = 'B';     break;
                case 0x21:      temp = 'C';     break;
                case 0x23:      temp = 'D';     break;
                case 0x24:      temp = 'E';     break;
                case 0x2B:      temp = 'F';     break;
                case 0x34:      temp = 'G';     break;
                case 0x33:      temp = 'H';     break;
                case 0x43:      temp = 'I';     break;
                case 0x3B:      temp = 'J';     break;
                case 0x42:      temp = 'K';     break;
                case 0x4B:      temp = 'L';     break;
                case 0x3A:      temp = 'M';     break;
                case 0x31:      temp = 'N';     break;
                case 0x44:      temp = 'O';     break;
                case 0x4D:      temp = 'P';     break;
                case 0x15:      temp = 'Q';     break;
                case 0x2D:      temp = 'R';     break;
                case 0x1B:      temp = 'S';     break;
                case 0x2C:      temp = 'T';     break;
                case 0x3C:      temp = 'U';     break;
                case 0x2A:      temp = 'V';     break;
                case 0x1D:      temp = 'W';     break;
                case 0x22:      temp = 'X';     break;
                case 0x35:      temp = 'Y';     break;
                case 0x1A:      temp = 'Z';     break;
                case 0x4E:      temp = '=';     break;
                case 0x55:      temp = '~'; break;
                case 0x6A:  temp = '|'; break;
                case 0x54:      temp = '`';     break;
                case 0x5B:      temp = '{';     break;
                case 0x4C:      temp = '+';     break;
                case 0x52:      temp = ':';     break;
                case 0x5D:      temp = '}';     break;
                case 0x41:      temp = '<';     break;
                case 0x49:      temp = '>';     break;
                case 0x4A:      temp = '?';     break;
                case 0x51:      temp = '_';     break;
                case 0x29:      temp = ' ';     break;
                case 0x71:      temp = '.';     break;
                case 0x7B:      temp = '-';     break;
                case 0x79:      temp = '+';     break;
                default:        temp = ScanData;  break;
        }
        
        return temp;
}

char NoShiftAscii(char ScanData)
{
        char temp;
        switch(ScanData)
        {       
                case 0x16:      temp = '1';     break;
                case 0x1E:      temp = '2';     break;
                case 0x26:      temp = '3';     break;
                case 0x25:      temp = '4';     break;
                case 0x2E:      temp = '5';     break;
                case 0x36:      temp = '6';     break;
                case 0x3D:      temp = '7';     break;
                case 0x3E:      temp = '8';     break;
                case 0x46:      temp = '9';     break;
                case 0x45:      temp = '0';     break;
                case 0x1C:      temp = 'a';     break;
                case 0x32:      temp = 'b';     break;
                case 0x21:      temp = 'c';     break;
                case 0x23:      temp = 'd';     break;
                case 0x24:      temp = 'e';     break;
                case 0x2B:      temp = 'f';     break;
                case 0x34:      temp = 'g';     break;
                case 0x33:      temp = 'h';     break;
                case 0x43:      temp = 'i';     break;
                case 0x3B:      temp = 'j';     break;
                case 0x42:      temp = 'k';     break;
                case 0x4B:      temp = 'l';     break;
                case 0x3A:      temp = 'm';     break;
                case 0x31:      temp = 'n';     break;
                case 0x44:      temp = 'o';     break;
                case 0x4D:      temp = 'p';     break;
                case 0x15:      temp = 'q';     break;
                case 0x2D:      temp = 'r';     break;
                case 0x1B:      temp = 's';     break;
                case 0x2C:      temp = 't';     break;
                case 0x3C:      temp = 'u';     break;
                case 0x2A:      temp = 'v';     break;
                case 0x1D:      temp = 'w';     break;
                case 0x22:      temp = 'x';     break;
                case 0x35:      temp = 'y';     break;
                case 0x1A:      temp = 'z';     break;
                case 0x4E:      temp = '-';     break;
                case 0x55:      temp = '^'; break;
                case 0x6A:  temp = 0x5C; break;// ¥
                case 0x54:      temp = '@';     break;
                case 0x5B:      temp = '[';     break;
                case 0x4C:      temp = ';';     break;
                case 0x52:      temp = ':';     break;
                case 0x5D:      temp = ']';     break;
                case 0x41:      temp = ',';     break;
                case 0x49:      temp = '.';     break;
                case 0x4A:      temp = '/';     break;
                case 0x51:      temp = 0x5C;    break;//¥
                case 0x29:      temp = ' ';     break;
                case 0x71:      temp = '.';     break;
                case 0x7B:      temp = '-';     break;
                case 0x79:      temp = '+';     break;
        
                default:        temp = ScanData;        break;
        }       
        return temp;
}

char ScanToAscii( char ScanData ,int Shift )
{
        char AsciiTemp;
        if(Shift == 1)
        {
                AsciiTemp = ShiftAscii(ScanData);
        }
        else AsciiTemp = NoShiftAscii(ScanData);
        return AsciiTemp ;
}
//---------------------------------------------------------------------------------------------
//**************************************
//インクルードファイル    1lcd_lib.c
//このファイルは後閑哲也さんが設計されたものです
//**************************************


///////////////////////////////////////////////
//  液晶表示器制御ライブラリ
//  内蔵関数は以下
//    lcd_init()    ----- 初期化
//    lcd_cmd(cmd)  ----- コマンド出力
//    lcd_data(chr) ----- 1文字表示出力
//    lcd_clear()   ----- 全消去



//////// データ出力サブ関数
void lcd_out(int code, int flag)
{
        output_x((code & 0xF0) | (input_x() & 0x0F));
        if (flag == 0)
                output_high(rs);                //表示データの場合
        else
                output_low(rs);                 //コマンドデータの場合
        delay_cycles(4);                        //NOP 1         
        output_high(stb);                       //strobe out
        delay_cycles(8);                        //NOP 2
        output_low(stb);                        //reset strobe
}
//////// 1文字表示関数
void lcd_data(int asci)
{
        lcd_out(asci, 0);                       //上位4ビット出力
        lcd_out(asci<<4, 0);            //下位4ビット出力
        delay_us(50);                           //50μsec待ち
}
/////// コマンド出力関数
void lcd_cmd(int cmd)
{
        lcd_out(cmd, 1);                        //上位4ビット出力
        lcd_out(cmd<<4, 1);                     //下位4ビット出力
        delay_ms(2);                            //2msec待ち
}
/////// 全消去関数
void lcd_clear()
{
        lcd_cmd(0x01);                          //初期化コマンド出力
        delay_ms(15);                           //15msec待ち
}
/////// 初期化関数
void lcd_init()
{
        set_tris_x(mode);                       //モードセット
        delay_ms(15);
        lcd_out(0x30, 1);                       //8bit mode set
        delay_ms(5);
        lcd_out(0x30, 1);                       //8bit mode set
        delay_ms(1);
        lcd_out(0x30, 1);                       //8bit mode set
        delay_ms(1);
        lcd_out(0x20, 1);                       //4bit mode set
        delay_ms(1);
        lcd_cmd(0x2E);                          //DL=0 4bit mode
        lcd_cmd(0x08);                          //display off C=D=B=0
        lcd_cmd(0x0D);                          //display on C=D=1 B=0
        lcd_cmd(0x06);                          //entry I/D=1 S=0
        lcd_cmd(0x02);                          //cursor home
}



//-----------------------------------------------------------------------------------------------

 <動作結果>
  ・以下に キーボードから”y”、”b ” と キーインして ”k” を押して指を離した場合の液晶表示を
   示します。